using System;
using System.Linq.Expressions;
using System.Reflection;
using Glue.Internals.Nulls;

namespace Glue.Internals
{
    internal class GenericExpression
    {
        private readonly AccessorFactory accessorFactory = new AccessorFactory();

        public String Name { get; private set; }
        public Expression SubExpression { get; private set; }
        public IObjectMemberAccessor Accessor { get; private set; }

        internal GenericExpression(Expression expression)
        {
            if (expression.NodeType == ExpressionType.MemberAccess)
                HandlePropertyOrField(expression);
            else if (expression.NodeType == ExpressionType.Call)
                HandleMethod(expression);
            else
                throw new GlueException("Neither a member or method expression. System fail. Contact the developers");

        }

        private void HandleMethod(Expression expression)
        {
            var methodCallExpression = (MethodCallExpression)expression;
            Name = methodCallExpression.Method.Name + "()";
            SubExpression = methodCallExpression.Object;
            Accessor = accessorFactory.Create(methodCallExpression.Method);
        }

        private void HandlePropertyOrField(Expression expression)
        {
            var memberExpression = (MemberExpression)expression;

            var innerExpression = memberExpression.Expression;

            if (innerExpression.NodeType == ExpressionType.Convert)
            {
                innerExpression = ((UnaryExpression)memberExpression.Expression).Operand;
            }

            var member = memberExpression.Member;
            var propertyInfo = (member as PropertyInfo).AsMaybe();
            Accessor = propertyInfo.HasValue()? 
                accessorFactory.Create(propertyInfo.GetValue()) : 
                accessorFactory.Create(member as FieldInfo);

            Name = memberExpression.Member.Name;
            SubExpression = innerExpression;
        }
    }
}